#include <GameEngine/Multimedia/music.h>



#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

#ifndef DOXYGEN_IGNORE
#include "music.moc"
#endif

GAMEENGINE_IMPLEMENT_RTTI(Music)

GAMEENGINE_IMPLEMENT_PTR_RESOURCE( Music::MusicResourceManager )

//========================================================


//! Allocate data
/*!
 *  The AllocatorMusic class allocates data using the libZPlay CreateZPlay() function
 *
 @param nb_elements The number of elements to allocate. This parameter is ignored by the
                    allocate function.
 */
libZPlay::ZPlay* AllocatorMusic::allocate(unsigned int nb_elements) {
	return libZPlay::CreateZPlay();
}

//! Desallocate data
/*!
 *  The AllocatorMusic class desallocates data using the libZPlay Release() function
 *
 @param item The music player to desallocate.
 @param nb_elements The number of elements to desallocate. This parameter is ignored by
                    the desallocate function.
 */
void AllocatorMusic::desallocate(libZPlay::ZPlay* item, unsigned int nb_elements) {
	item->Release();
}


//========================================================


//! Default constructor
Music::Music() : Music::RESOURCE_TYPE(1,true,rtti()) {
	create_id();
}

//! Constructor from a file path
Music::Music(const GEString& file) : Music::RESOURCE_TYPE(1,true,rtti()) {
	create_id();
	if ( id_ == nil ) return;
	id_->OpenFile(file.c_str(), libZPlay::sfAutodetect);
}

//! Copy constructor
/*!
 @param rhs The music to copy
 */
Music::Music(const Music& rhs) : Music::RESOURCE_TYPE(rhs) {
	context_ = rtti();
}

//! Destructor
Music::~Music() {
	clean_id();
}

//! Assignment operator
/*!
 @param rhs The music to copy
 */
Music& Music::operator=(const Music& rhs) {
	if ( &rhs != this ) {
		MusicResourceManager* manager = (MusicResourceManager*)MusicResourceManager::instance();
		if ( id_ != nil ) manager->unreference_resource(*this);
		context_ = rtti();
		id_ = rhs.id_;
		if ( id_ != nil ) manager->reference_resource(*this);
	}
	return *this;
}

//! Start or resume playing the music at the current position in the file
/*!
 @return True if music playing could be started/resumed, false otherwise
 */
bool Music::play() const {
	if ( id_ == nil ) return false;
	return ( id_->Play() == 1 );
}

//! Resume playing the music at the current position in the file
/*!
 @return True if music playing could be resumed, false otherwise
 @note This function is quite similar to the play() function, except
       that non-paused musics will not start with the resume() function
 */
bool Music::resume() const {
	if ( id_ == nil ) return false;
	return ( id_->Resume() == 1 );
}

//! Pause the music
/*!
 @return True if music could be paused, false otherwise
 */
bool Music::pause() const {
	if ( id_ == nil ) return false;
	return ( id_->Pause() == 1 );
}

//! Stop the music
/*!
 @return True if music could be stopped, false otherwise
 */
bool Music::stop() const {
	if ( id_ == nil ) return false;
	return ( id_->Stop() == 1 );
}

//! Change the file to be played
/*!
 *  If music was playing, this will stop the previous music and start the
 *  new music automatically (providing that the file is valid)
 *
 @param file The file to be played, provided with its full path. Path
             can either be absolute or relative
 @return True if file could be changed, false otherwise
 */
bool Music::set_file(const GEString& file) const {
	if ( id_ == nil ) return false;

	// Get current state
	bool p = ( state() == Playing );

	// Try to open new file
	bool res = ( id_->OpenFile(file.c_str(), libZPlay::sfAutodetect) == 1 );
	if ( res == true ) {
		if ( p ) play();
	}
	return res;
}

//! Change the volume of the music
/*!
 *  This operation can be executed while the music is playing
 *
 @param volume The new volume of the music. Volume is expected to be
               provided between 0 (music cannot be heard) and 100
			   (music is played at its full volume)
 @return True if volume could be changed, false otherwise
 */
bool Music::set_volume(int volume) const {
	if ( id_ == nil ) return false;
	return ( id_->SetPlayerVolume(volume, volume) == 1 );
}

//! Check if music is currently being played
/*!
 @return True if music is currently being played, false otherwise
 */
Music::PlayerState Music::state() const {
	if ( id_ == nil ) return Invalid;
	libZPlay::TStreamStatus status;
	id_->GetStatus(&status);
	if ( status.fPause != 0 ) return Paused;
	if ( status.fPlay != 0 ) return Playing;
	return Stopped;
}

//! Return the music context
Music::CONTEXTID Music::context() const {
	return rtti_id_;
}



//========================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
